#ifndef __loongarch_h_
#define __loongarch_h_

#include <larchintrin.h>

// 龙芯架构控制状态寄存器，此处的 CSR 寄存器宏定义主要用于速查

#define LOONGARCH_CSR_CRMD 0x0   // 当前模式信息
#define LOONGARCH_CSR_PRMD 0x1   // 例外前模式信息
#define LOONGARCH_CSR_EUEN 0x2   // 扩展部件使能
#define LOONGARCH_CSR_MISC 0x3   // 杂项控制
#define LOONGARCH_CSR_ECFG 0x4   // 例外配置
#define LOONGARCH_CSR_ESTAT 0x5  // 例外状态
#define LOONGARCH_CSR_ERA 0x6    // 例外返回地址
#define LOONGARCH_CSR_BADV 0x7   // 出错虚地址
#define LOONGARCH_CSR_BADI 0x8   // 出错指令
#define LOONGARCH_CSR_EENTRY 0xc // 例外入口地址

// 读 CSR.CRMD 当前模式信息
static inline unsigned int r_csr_crmd() {
	unsigned int x;
	asm volatile("csrrd %0, 0x0" : "=r"(x));
	return x;
}

// 写 CSR.CRMD 当前模式信息
static inline void w_csr_crmd(unsigned int x) {
	asm volatile("csrwr %0, 0x0" : : "r"(x));
}

// 读 CSR.PRMD 例外前模式信息
static inline unsigned int r_csr_prmd() {
	unsigned int x;
	asm volatile("csrrd %0, 0x1" : "=r"(x));
	return x;
}

// 写 CSR.PRMD 例外前模式信息
static inline void w_csr_prmd(unsigned int x) {
	asm volatile("csrwr %0, 0x1" : : "r"(x));
}

// 读 CSR.ECFG 例外配置
static inline unsigned int r_csr_ecfg() {
	unsigned int x;
	asm volatile("csrrd %0, 0x4" : "=r"(x));
	return x;
}

// 写 CSR.ECFG 例外配置
static inline void w_csr_ecfg(unsigned int x) {
	asm volatile("csrwr %0, 0x4" : : "r"(x));
}

// ESTAT 的 16..21 位是 ecode 字段（例外类型编码），值含义查表 7-8
// 例：判断系统调用例外 if (((r_csr_estat() & CSR_ESTAT_ECODE) >> 16) == 0xb)
#define CSR_ESTAT_ECODE (0x3fU << 16)

// 读 CSR.ESTAT 例外状态
static inline unsigned int r_csr_estat() {
	unsigned int x;
	asm volatile("csrrd %0, 0x5" : "=r"(x));
	return x;
}

// 读 CSR.ERA 例外返回地址
static inline unsigned long r_csr_era() {
	unsigned long x;
	asm volatile("csrrd %0, 0x6" : "=r"(x));
	return x;
}

// 写 CSR.ERA 例外返回地址
static inline void w_csr_era(unsigned long x) {
	asm volatile("csrwr %0, 0x6" : : "r"(x));
}

// 读取出错指令
static inline unsigned int r_csr_badi() {
	unsigned int x;
	asm volatile("csrrd %0, 0x8" : "=r" (x) );
	return x;
}

// 读例外入口地址
static inline unsigned long r_csr_eentry() {
	unsigned long x;
	asm volatile("csrrd %0, 0xc" : "=r"(x));
	return x;
}

// 写例外入口地址
static inline void w_csr_eentry(unsigned long x) {
	asm volatile("csrwr %0, 0xc" : : "r"(x));
}

// PRMD_PPLV 和 PRMD_PIE 的用法是
//	1. 按位与 & CSR.PRMD 可以判断例外前特权级和中断使能位
//	2. 按位或 | CSR.PRMD 可以直接变 PLV3 和开中断
//	以此类推
#define PRMD_PPLV (3U << 0) // 例外前特权级
#define PRMD_PIE (1U << 2)  // 例外前中断使能

// TLB 相关控制状态寄存器

#define LOONGARCH_CSR_TLBIDX 0x10  // TLB 索引
#define LOONGARCH_CSR_TLBEHI 0x11  // TLB 表项高位
#define LOONGARCH_CSR_TLBELO0 0x12 // TLB 表项低位 0
#define LOONGARCH_CSR_TLBELO1 0x13 // TLB 表项低位 1
#define LOONGARCH_CSR_ASID 0x18    // 地址空间标识符
#define LOONGARCH_CSR_PGDL 0x19    // 低半地址空间全局目录基址
#define LOONGARCH_CSR_PGDH 0x1a    // 高半地址空间全局目录基址
#define LOONGARCH_CSR_PGD 0x1b     // 全局目录基址
#define LOONGARCH_CSR_PWCL 0x1c	   // 页表遍历控制低半部分
#define LOONGARCH_CSR_PWCH 0x1d	   // 页表遍历控制高半部分
#define LOONGARCH_CSR_STLBPS 0x1e  // STLB 页大小

// 多级页表结构控制
#define PTBASE 12U		// 末级页表起始地址
#define PTWIDTH 9U		// 末级页表索引位数
#define DIR1BASE 21U	// 最低一级目录起始地址
#define DIR1WIDTH 9U	// 最低一级目录索引位数
#define DIR2BASE 30U	// 次低一级目录起始地址
#define DIR2WIDTH 9U	// 次低一级目录索引位数
#define PTEWIDTH 0U		// 内存中的页表项位宽，0 表示 64 bits
#define DIR3BASE 39U	// 次高一级目录起始地址
#define DIR3WIDTH 9U	// 次高一级目录索引位数
#define DIR4BASE  0U	// 最高一级目录起始地址
#define DIR4WIDTH 0U	// 最高一级目录索引位数

// 直接映射窗口的虚拟地址掩码
#define DMWIN_MASK 0x9000000000000000

// 页大小 (bytes)
#define PGSIZE 4096

// 页内偏移量的位数
#define PGSHIFT 12

// 低半虚拟地址空间
#define MAXVA (1L << (48 - 1))

// 对页舍入取 floor() 和 ceil()，保证分配/回收的物理地址对齐页大小
#define PGROUNDUP(sz) (((sz) + PGSIZE - 1) & ~(PGSIZE - 1))
#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE - 1))

// 页表各位含义
#define PTE_V (1UL << 0)  		// valid
#define PTE_D (1UL << 1)  		// dirty
#define PTE_PLV (3UL << 2)		// privilege level
#define PTE_MAT (1UL << 4)		// memory access type
#define PTE_P (1UL << 7)  		// physical page exists
#define PTE_W (1UL << 8)  		// writeable
#define PTE_NX (1UL << 62)		// non executable
#define PTE_NR (1UL << 61)		// non readable
#define PTE_RPLV (1UL << 63)	// restricted privilege level enable

// 0x F FFFF FFFF (基本页页表 36 位物理地址) << 12
#define PAMASK 0xFFFFFFFFFUL << PGSHIFT

// PTE 值转换为物理地址
#define PTE2PA(pte) (pte & PAMASK)

// 物理地址到 PTE 的对应位置
#define PA2PTE(pa) (((unsigned long)pa) & PAMASK)

#define PTE_FLAGS(pte) ((pte) & 0xE0000000000001FFUL)
#define PXMASK 0x1FF // 9 bits
#define PXSHIFT(level) (PGSHIFT + (9 * (level)))

// 从虚拟地址 va 中提取 level 级别的 9 位页表索引
#define PX(level, va) ((((unsigned long)(va)) >> PXSHIFT(level)) & PXMASK)

// 写地址空间标识符
static inline void w_csr_asid(unsigned int x) {
	asm volatile("csrwr %0, 0x18" : : "r"(x));
}

// 写 STLB 页大小
static inline void w_csr_stlbps(unsigned int x) {
	asm volatile("csrwr %0, 0x1e" : : "r"(x));
}

#define LOONGARCH_CSR_CPUID 0x20 // 处理器编号
#define LOONGARCH_CSR_SAVE0 0x30 // 数据保存寄存器 0，用于软件临时保存寄存器的值
#define LOONGARCH_CSR_TID 0x40   // 定时器编号
#define LOONGARCH_CSR_TCFG 0x41  // 定时器配置
#define LOONGARCH_CSR_TVAL 0x42  // 定时器值
#define LOONGARCH_CSR_CNTC 0x43  // 计时器补偿
#define LOONGARCH_CSR_TICLR 0x44 // 定时中断清除

// 例外和中断的入口"间距"
// 当 16..18 位都 == 0时，所有中断共享同一个入口
#define CSR_ECFG_VS_SHIFT 16
#define HWI_VEC 0x3fcU          	// 8 个外部硬件中断 (0x3fcU = 0011 1111 1100b)
#define CSR_ECFG_LIE_TI_SHIFT 11	// 时钟中断位
#define TI_VEC (0x1U << CSR_ECFG_LIE_TI_SHIFT)

#define CSR_TICLR_CLR (0x1U << 0)	// 时钟中断清空
#define CSR_TCFG_EN (1U << 0)    	// 时钟使能位
#define CSR_TCFG_PER (1U << 1)   	// 时钟周期

// 写定时器配置
static inline void w_csr_tcfg(unsigned long x) {
	asm volatile("csrwr %0, 0x41" : : "r"(x));
}

// 读定时中断清除
static inline unsigned int r_csr_ticlr() {
	unsigned int x;
	asm volatile("csrrd %0, 0x44" : "=r"(x));
	return x;
}

// 写定时中断清除
static inline void w_csr_ticlr(unsigned int x) {
	asm volatile("csrwr %0, 0x44" : : "r"(x));
}

#define LOONGARCH_CSR_TLBRENTRY 0x88 // TLB 重填例外入口地址
#define LOONGARCH_CSR_TLBRBADV 0x89  // TLB 重填例外出错虚地址
#define LOONGARCH_CSR_TLBRERA 0x8a   // TLB 重填例外返回地址
#define LOONGARCH_CSR_TLBRSAVE 0x8b  // TLB 重填例外数据保存
#define LOONGARCH_CSR_TLBRELO0 0x8c  // TLB 重填例外表项低位 0
#define LOONGARCH_CSR_TLBRELO1 0x8d  // TLB 重填例外表项低位 1
#define LOONGARCH_CSR_TLBREHI 0x8e   // TLB 重填例外表项高位
#define LOONGARCH_CSR_TLBRPRMD 0x8f  // TLB 重填例外前模式信息
#define LOONGARCH_CSR_MERRENTRY 0x93 // 机器错误例外入口地址

// 写 TLB 重填例外入口地址
static inline void w_csr_tlbrentry(unsigned long x) {
	asm volatile("csrwr %0, 0x88" : : "r"(x));
}

// 读 TLB 重填例外表项低位 0
static inline unsigned long r_csr_tlbrelo0() {
	unsigned long x;
	asm volatile("csrrd %0, 0x8c" : "=r"(x));
	return x;
}

// 读 TLB 重填例外表项低位 1
static inline unsigned long r_csr_tlbrelo1() {
	unsigned long x;
	asm volatile("csrrd %0, 0x8d" : "=r"(x));
	return x;
}

// 写机器错误例外入口地址
static inline void w_csr_merrentry(unsigned long x) {
	asm volatile("csrwr %0, 0x93" : : "r"(x));
}

static inline void w_csr_tlbrehi(unsigned long x) {
	asm volatile("csrwr %0, 0x8e" : : "r"(x));
}

// 读低半地址空间全局目录基址
static inline unsigned long r_csr_pgdl() {
	unsigned long x;
	asm volatile("csrrd %0, 0x19" : "=r"(x));
	return x;
}

// 写低半地址空间全局目录基址
static inline void w_csr_pgdl(unsigned long x) {
	asm volatile("csrwr %0, 0x19" : : "r"(x));
}

// 读高半地址空间全局目录基址
static inline unsigned long r_csr_pgdh() {
	unsigned long x;
	asm volatile("csrrd %0, 0x1a" : "=r"(x));
	return x;
}

// 写高半地址空间全局目录基址
static inline void w_csr_pgdh(unsigned long x) {
	asm volatile("csrwr %0, 0x1a" : : "r"(x));
}

// 写页表遍历控制低半部分
static inline void w_csr_pwcl(unsigned int x) {
	asm volatile("csrwr %0, 0x1c" : : "r"(x));
}

// 写页表遍历控制高半部分
static inline void w_csr_pwch(unsigned int x) {
	asm volatile("csrwr %0, 0x1d" : : "r"(x));
}

// 读 sp 寄存器（栈指针值）
static inline unsigned long r_sp()
{
	unsigned long x;
	asm volatile("addi.d %0, $sp, 0" : "=r" (x) );
	return x;
}

// 读 tp 寄存器值，tp 保存线程指针, 即该处理器核的 hartid (core number
// 此处用该值作为 cpus[] 数组下标
static inline unsigned long r_tp()
{
	unsigned long x;
	asm volatile("addi.d %0, $tp, 0" : "=r" (x) );
	return x;
}

// 直接映射配置窗口 0 - 3
#define LOONGARCH_CSR_DMWIN0 0x180 // 64 位直接映射窗口 0: loadstore操作 & 取指
#define LOONGARCH_CSR_DMWIN1 0x181 // 64 位直接映射窗口 1: loadstore操作 & 取指
#define LOONGARCH_CSR_DMWIN2 0x182 // 64 位直接映射窗口 2: loadstore操作
#define LOONGARCH_CSR_DMWIN3 0x183 // 64 位直接映射窗口 3: loadstore操作

// extioi 寄存器
#define LOONGARCH_IOCSR_EXTIOI_EN_BASE 0x1600
#define LOONGARCH_IOCSR_EXTIOI_ISR_BASE 0x1800
#define LOONGARCH_IOCSR_EXTIOI_MAP_BASE 0x14c0
#define LOONGARCH_IOCSR_EXTIOI_ROUTE_BASE 0x1c00
#define LOONGARCH_IOCSR_EXRIOI_NODETYPE_BASE 0x14a0

// IOCSR 读写

static inline unsigned int iocsr_readl(unsigned int reg) {
	return __iocsrrd_w(reg);
}

static inline unsigned long iocsr_readq(unsigned int reg) {
	return __iocsrrd_d(reg);
}

static inline void iocsr_writel(unsigned int val, unsigned int reg) {
	__iocsrwr_w(val, reg);
}

static inline void iocsr_writeq(unsigned long val, unsigned int reg) {
	__iocsrwr_d(val, reg);
}

// 中断使能位
#define CSR_CRMD_IE_SHIFT 2
#define CSR_CRMD_IE (0x1U << CSR_CRMD_IE_SHIFT)

// 查中断情况
static inline int intr_get() {
	unsigned int x = r_csr_crmd();
	return (x & CSR_CRMD_IE) != 0;
}

// 开启设备中断
static inline void intr_on() { 
	w_csr_crmd(r_csr_crmd() | CSR_CRMD_IE); 
}

// 禁用设备中断
static inline void intr_off() { 
	w_csr_crmd(r_csr_crmd() & ~CSR_CRMD_IE); 
}

#endif
